home *** CD-ROM | disk | FTP | other *** search
- Newsgroups: comp.lang.ada,comp.lang.c++
- Path: in2.uu.net!world!bobduff
- From: bobduff@world.std.com (Robert A Duff)
- Subject: Re: some questions re. Ada/GNAT from a C++/GCC user
- Message-ID: <Dp1oAw.7Cz@world.std.com>
- Bcc: bobduff
- Organization: The World Public Access UNIX, Brookline, MA
- References: <wnewmanDoxrCp.DKv@netcom.com>
- Date: Fri, 29 Mar 1996 19:47:20 GMT
-
- In article <wnewmanDoxrCp.DKv@netcom.com>,
- Bill Newman <wnewman@netcom.com> wrote:
- >... (I hope against hope that
- > the tone of the answers will resemble the cross-language comparisons
- > in _Ada as a Second Language_ more closely than it resembles the
- > recent C++/Ada flamewar.)
-
- ;-)
-
- Did you notice the recent Eiffel/Ada flamewar? It was much more
- reasonable -- not even a flamewar, really, but a technical argument.
- It *is* possible!
-
- > Does GNAT completely implement generics as defined in the standard?
- > (I ask because I have heard that no compiler, G++ otherwise, has yet
- > implemented C++ templates completely, and the G++ implementation
- > caused me lots of hassles before 2.7.x, and still causes some hassles
- > now.)
- >
- > I *assume* that GNAT supports exceptions completely since they're an integral
- > part of the language and I didn't see any disclaimers, but since AFAIK G++
- > doesn't do them very well, I'd like to double-check: how well does
- > GNAT do exceptions?
-
- Generics and exceptions are both standard parts of Ada, so anything that
- calls itself an Ada compiler implements them. Furthermore, these
- features have been around since Ada 83 (although a few bells and
- whistles were added in Ada 95). Any compiler can have bugs, of course.
- But the situation with C++, where templates and exceptions are pretty
- new, and not officially standardized, isn't true of Ada.
-
- > I didn't notice anything about garbage collection in the GNAT docs, so
- > I assume it doesn't support it. Will GNAT support GC in the
- > foreseeable future?
-
- GNAT does not support GC. I believe somebody is working on adding GC to
- GNAT. Also, the Intermetrics Ada compiler supports GC.
-
- > How well does GDB work with GNAT output?
-
- I've found it to be somewhat painful. However, it does work, and the
- Ada support is being improved.
-
- >...Is it possible to get GDB to
- > interactively call arbitrary procedures/functions from GNAT-generated
- > code?
-
- Yes, I do that a lot.
-
- > Is there any way in Ada to iterate abstractly over the contents of a
- > container,...
-
- Several ways:
-
- - Write a generic procedure Iterate, which takes a formal
- procedure representing the body of the loop -- that is,
- the formal procedure says what to do for each element.
-
- - Write a procedure that takes an access-to-procedure as
- a parameter. In standard Ada, this only works if the
- procedure being passed in is non-nested, which makes this
- technique fairly useless. In GNAT, you can "cheat", by
- using 'Unrestricted_Access, but that's not standard Ada.
-
- - Write an Iterator class. Define any particular loop
- by deriving from that, and overriding the Do_One_Element
- method, or whatever. I think there's an example in the
- Rationale.
-
- - For each data structure, define Start, Done, Current_Element,
- and Get_Next operations. Or some variation on this.
-
- None of these is as pretty as the Sather solution, but they achieve the
- main goal, which is to have the module that defines the data structure
- define how to loop, and the clients define what they do at each
- iteration.
-
- >... i.e. without writing each loop in a way which depends
- > strongly on the implementation of the container? I know I
- > could define a container class Foo_Basket_Type which would
- > let me do something like
- > for I = 1 .. Size(Foo_Basket) loop -- class implemented as array
- > Sum := Sum + Bletchery(Element(Foo_Basket, I));
- > end loop;
- > or
- > I : Foo_Basket_Iterator_Type := Head(Foo_Basket);
- > ..
- > while not Is_Done(I) loop -- class implemented as list
- > Sum := Sum + Bletchery(dereference I);
- > I := Next(I);
- > end loop;
-
- It seems like the above does not have to imply "class implemented as
- list". So just write all your loops like that, whether the
- implementation is a list, or an array, or whatever. You can do this in
- most languages. This is what I was suggesting in the fourth item above.
-
- > but what I'd really like to is something like
- > for each I in Foo_Basket loop -- don't much care how class is implemented
- > Sum := Sum + Bletchery(dereference I);
- > end loop;
- > I understand that in languages like Sather, I could do this without
- > macros. In G++ I can come pretty close to this with CPP macros:
- > FOR_EACH(i, foo_basket)
- > sum += bletchery(*i);
- > In standard C++ (without the G++ `typeof' operator) I'd write something like
- > FOR_EACH(i, Foo_Basket::Iterator, foo_basket)
- > sum += bletchery(*i);
- > I use this idiom a lot -- about once per 40 lines of code in my
- > current project. I much prefer it to the alternative of making my
- > code depend on the implementation of the container.
-
- I don't agree that the only alternative to macros is to break the
- abstraction. The techniques I mentioned above also achieve your goal,
- although they are admittedly somewhat ugly. Macros can add syntactic
- sugar to those techniques, but don't fundamentally change things.
-
- >...Is there any way of
- > doing something like this in Ada? (I imagine there is, since the
- > alternatives look unnecessarily difficult to maintain.)
-
- I'll note that the alternatives I gave could be done in C++, too,
- without macros.
-
- > Due in part to my problems with GDB mentioned above, my C++ programs
- > tend to contain a lot of calls to macros MUTTER1, MUTTER2,
- > etc. defined either as no-op (for low levels of verbosity) or as
- > #define MUTTER1(x) do { cerr << mutter_prefix << x << '\n'; } while (false)
- > so that I can write things like
- > MUTTER1("done with sampling, w = " << w << ", table = " << table);
- > concisely. It's my impression that Ada's I/O facilities make it hard
- > to do trivial things like this concisely. Is this wrong? I don't really
- > want to have to write
- > if (Global_Verbosity >= 1) then
- > Write_Mutter_Indent(Global_Mutter);
- > Write(Global_Mutter, "done with sampling, W = ");
- > Write(Global_Mutter, W);
- > Write(Global_Mutter, ", table = ");
- > Write(Global_Mutter, Table);
- > Write_Line(Global_Error); -- silly typo encouraged by excess verbosity
- > end if;
- > for a simple statement like the one above.
-
- The usual thing is to write something like:
-
- Put_Line("done with sampling, W = " & Image(W)
- & ", table = " & Image(Table));
-
- For simple data types, there is the 'Image attribute. For others, you
- have to write your own Image function, which converts it to a string.
- You said you wanted to do that anyway. If you don't like
- Text_IO.Put_Line, write your own: Two procedures for each verbosity
- level, and one of those two can add the new-line. Or maybe you always
- want a new-line. Or maybe the verbosity should be passed as a
- parameter, with a default. I can imagine many variations.
-
- Some people go even further, and define overloaded versions of "&" that
- do both the concatenation and the conversion-to-string. Seems like
- overkill to me, but some people like it.
-
- > When I make two different instantiations of a generic package with the
- > same arguments, I understand the compiler treats them formally as two
- > different packages, which is OK with me. However, I'd appreciate
- > knowing the compiler wouldn't actually output two redundant copies of
- > the corresponding (identical?) machine code, but instead share the
- > code. I saw somewhere that the compiler is given considerable freedom
- > to share one instantiation between several arguments if it thinks it's
- > appropriate, which is also OK with me. However, I haven't seen any
- > guarantee that the compiler won't output redundant copies for
- > instantiations with identical arguments. Is there such a guarantee?
-
- No. Some compilers do it, some don't. Currently, GNAT does not.
-
- If you want to avoid code bloat, you can usually arrange for *most* of
- the code in a generic to be separated out into a non-generic package.
- Sometimes, this requires low-level hacks, but the low-level hacks are
- hidden inside the generic and its helper package, so they need not
- concern clients.
-
- > My examples of `for each' and `mutter' above share a common feature:
- > I'd like to abstract away a common pattern so that I only need to type
- > each argument once, and I haven't figured out how to do it in Ada.
-
- Macros can be used to achieve that, but in many cases, so can
- procedures, generics, Sather's iterators, etc. IMHO, the more
- restricted features are generally better than macros. Macros are more
- powerful, but also more difficult to understand. TeX is a language that
- uses macros for just about everything, and I find TeX code to be totally
- incomprehensible, despite the fact that I've read the TeX Book 4 times.
-
- >...I
- > don't object to the rest of the verbosity I have seen in Ada: I can
- > see that it might make programs more readable, and since it's checked
- > by the compiler it shouldn't make programs significantly harder to
- > maintain. However, the kind of verbosity required to hand-code
- > patterns like `for each' and `mutter' does not seem to make code more
- > readable, and since it is not checked by the compiler I'm afraid it
- > might cause maintenance problems by making it possible to add bugs by
- > changing some but not all occurrences of the redundant terms. Are
- > there ways (e.g. in the cases described above, or other cases that I
- > haven't thought of yet) in which Ada will force me to do this kind of
- > thing where C++ wouldn't? If so, is this a bad thing (as I suspect),
- > or is it a positive feature in some sense that I haven't figured out,
- > or is it just the price we pay for the benefit of being guaranteed
- > that no one has abused macros in the program?
-
- I think the last phrase ("price we pay...") is accurate. I actually
- think macros would be nice to have in some rare cases. But in *most*
- cases, if you're using macros, that's an indication of a language flaw
- -- you didn't have a higher-level feature that got the job done, so you
- had to resort to macros.
-
- You'll find a lot of anti-macro fanatics in the Ada world.
-
- > Why doesn't Ada 95 allow declarations to be interspersed with ordinary
- > statements as C++ does? (Or does it? _Ada as a Second Language_ is a
- > big book!) It seems to me that the C++ approach is a small but
- > definite win. Does it interact very badly somehow with all those
- > guarantees on elaboration order?
-
- To intersperse declarations, you have to use a block_statement, like
- this:
-
- for I in Some_String'Range loop -- I wish I could easily use an iterator here ;-)
- declare
- X: constant Character := Some_String(I);
- begin
- ...
- end;
- end loop;
-
- To me, the "declare", "begin", and "end" are just useless verbosity.
- I prefer the C++ rule.
-
- As to WHY the Ada syntax is this way, I'm not sure. It's partly because
- it was inherited from Pascal. In Pascal, "begin" separates purely
- compile-time declarative stuff from purely run-time algorithmic stuff,
- so the "begin" makes some conceptual sense. But in Ada, the
- declarations can do all kinds of run-time stuff, so putting "begin" in
- between declarations and statements makes less sense. Pascal doesn't
- have block_statements, but the syntax of Ada's block_statements comes
- from the same Pascal heritage.
-
- The "begin" also makes a difference for exception handling and tasking.
- But I don't find these reasons compelling, either.
-
- > Someone remarked -- in this newsgroup recently IIRC -- that macros
- > were explicitly disallowed in the design goals for Ada (Ada 83?),
- > since they make programs hard to understand. I don't remember the
- > exact wording, but as I remember the key reason was that you would
- > never know what was a macro and what wasn't without reading the entire
- > program hunting for macro definitions. (I searched for `macro' in the
- > Rationale without success, so I'm just going on speculation and dim
- > memory here.) It seems to me that that is a funny objection: most
- > macro processors these days don't require a characteristic pattern to
- > introduce macro expansions, but as far as I can tell there's no reason
- > that you couldn't restrict macro expansion to e.g. patterns preceded
- > by the keyword `macro'.
-
- I don't think this is the main reason people think macros are hard to
- understand. As you say, it would be easy to design the syntax so that a
- macro invokation looks different from anything else.
-
- Macros are generally hard to understand for other reasons, I think.
- Part of the problem is the low-level character-based nature of macros.
- But as you point out below, that's not true of all known macro
- facilities. Part of the problem is the lack of scoping rules -- if a
- macro expansion refers to a global variable X, it's referring to
- whatever X happens to be lying around at the point of the macro
- expansion. Contrast that with Ada's generics, where the names are bound
- at the site of the generic itself. Part of the problem is that you have
- to imagine what the macro expands to in order to understand what it
- does, and complex code trasformations are hard to imagine accurately.
- TeX macros have additional problems, like the fact that you can't tell
- at the call site how many arguments are being passed -- in fact, you
- can't in general tell until run time.
-
- >... Granted, C's macro facilities are fairly
- > disgusting and inconsistent with the design goals of Ada, but it seems
- > to me a macro facility (or `generic syntax' facility?) more consistent
- > with Ada could be useful -- perhaps something closer to Scheme's
- > `hygienic macros' than to C's macro preprocessor. Some restrictions
- > (e.g. only allowing macros to expand into expressions, sequences of
- > statements, or sequences of declarations, and not into arbitrary textual
- > strings) and perhaps importing from Lisp the idea that macros are
- > transformations on patterns of language tokens and expressions, rather
- > than transformations on patterns of characters) could do a pretty
- > decent job of discouraging people from writing truly screwy macros
-
- I agree. Lisp macros are much less painful than character-level macros.
-
- > without forbidding useful macros like `For_Each' or (to generalize
- > `MUTTER') `Write_Sequence' (sending a bunch of items to the same
- > stream, and using the language's overloading facility to sort out
- > which version of the Write procedure to call for each).
-
- If you complain that you want macros to do For_Each, I'll answer by
- giving you iterators, not by giving you macros. Same for Write_Sequence
- -- better to use a procedure call than a macro call, IMHO.
-
- >...Would this
- > have been a good idea, except perhaps that the language is too
- > complicated already, or would this be a bad idea for some more
- > fundamental reason?
-
- I think most Ada people would say that macros inherently cause
- hard-to-understand code, and therefore should be abolished. I don't
- agree. I would like to have a macro facility (at a higher level than
- character-level), but I would use it rarely.
-
- > Finally, I found it intriguing when someone (somewhere in the endless
- > C++ vs. Ada thread) described a program which used exceptions to
- > convert runtime errors to `graceful degradation' so successfully that
- > the program (for fire control?!) continued operating more-or-less
- > correctly despite numerous bugs. ...
-
- I'm not impressed by that example. It's true that exceptions can be
- useful in designing a fault-tolerant system, but fault-tolerance is
- still very hard, and needs to be very carefully designed. For *most*
- programs, the primary purpose of exceptions, most of the time, is to
- detect bugs as early as possible, and the response is to fix the bug,
- not to allow the program to muddle along in a confused state.
-
- - Bob
-
- P.S. I hope you enjoy Ada. Why not get a copy of GNAT, and try it out?
-